/**
 * 给定N个操作，每个操作格式为:a b d
 * 表示 Xa - Xb = d
 * 其中a、b在[1...N]
 * 依次对每个操作，问到该操作为止，是否存在满足条件的序列
 * 如果存在，则该操作对后续一直有效；否则忽略该操作
 * 要求输出所有生效的操作
 * 使用带权并查集
 */
#include <bits/stdc++.h>
using namespace std;

#include <bits/extc++.h>
using namespace __gnu_pbds;

using llt = long long;
using vi = vector<int>;

struct uf_t{

vector<int> father;
vector<llt> w; // wi表示i到树根的距离

void init(int n){father.assign(n + 1, 0); w.assign(n + 1, 0); for(int i=1;i<=n;++i)father[i]=i;}

int find(int x){
    if(father[x] == x) return x;
    auto t = find(father[x]);
    /// 此时 w[father[x]]是father到树根的距离
    /// 更新w[x]并将x直接接到树根
    w[x] += w[father[x]]; 
    return father[x] = t;
}

/// x到树根的距离比y到树根的距离多d
bool unite(int x, int y, llt d){
    auto fx = find(x);
    auto fy = find(y);
    /// 如果x和y本来就在同一树上，直接检查即可
    if(fx == fy) return w[x] == w[y] + d;
    /// 否则，将x树接进y树，为了满足条件，有
    /// w[fx] + w[x] = w[y] + d
    /// 因此可以算出w[fx]
    father[fx] = fy;
    w[fx] = w[y] + d - w[x];
    return true;
}

}UF;

int N, Q;

int main(){
#ifndef ONLINE_JUDGE
    freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(0);
    cin >> N >> Q;
    UF.init(N);
    for(int a,b,d,i=1;i<=Q;++i){
        cin >> a >> b >> d;
        auto flag = UF.unite(a, b, d);
        if(flag) cout << i << " ";
    }
    cout << endl;
    return 0;
}